﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using DSRSystemCore.Core;
using MongoDB.Driver;
using MongoDB.Driver.Builders;
using MongoDB.Driver.Linq;

namespace CompanySearchConsole
{
    public class TestSuite
    {
        
        MongoDatabase resultsDb;
        ITestContext testContext;
        public int repeatNo = 20;

        public TestSuite(MongoDatabase resultDb, ITestContext mongoContext)
        {
            // TODO: Complete member initialization
            this.resultsDb = resultDb;
            this.testContext = mongoContext;
        }

        public void ExecuteTests(int pNo)
        {
            List<Task> tasks = new List<Task>();
            ConcurrentBag<List<MongoEntities.RawQueryResult>> participantResults = new ConcurrentBag<List<MongoEntities.RawQueryResult>>();

            MongoEntities.Session session = CreateNewSession(pNo);
        

            for (int k = 0; k < repeatNo; k++)
            {
                using (Barrier barrier = new Barrier(session.ParticipantsNo))
                {

                    for (var i = 1; i <= session.ParticipantsNo; i++)
                    {
                        int j = i;
                        tasks.Add(Task.Factory.StartNew(() =>
                        {
                            var listList = new List<List<MongoEntities.RawQueryResult>>();
                            var participantList = new List<MongoEntities.RawQueryResult>();

                            participantResults.Add(Test(j - 1, string.Format("{0}:{1}", j, k), barrier, session, testContext));

                            // participantResults.Add(Test(j - 1, string.Format("{0}:{1}", j, 1), barrier, session));

                            //for (int k = 0; k < 20; k++)
                            //{
                            //   listList.Add(Test(j-1, string.Format("{0}:{1}", j, k), barrier, session));

                            //}



                            /* for (var index = 0; index < listList[0].Count; index++)
                             {
                                 var txt = listList[0][index].Info;


                                 var items = listList.Select(list => list.First(qr => qr.Info == txt));

                                 //var avg = listList.Select(list => list.First(qr => qr.Info == txt).Duration).Average();
                                 //var x = items.Where(qr => qr.Duration >= avg).OrderBy((a) => a.Duration);
                                 //participantList.Add(x.First());

                                 var x = items.OrderBy((a) => a.Duration);
                                 participantList.Add(x.Last());

                             }

                             participantResults.Add(participantList);*/
                        }
                        ));

                    }
                    Task.WaitAll(tasks.ToArray());
                }
            }

            var dbResults = GetMongoDatabase().GetCollection<MongoEntities.RawQueryResult>(typeof(MongoEntities.RawQueryResult).Name);

            var results = participantResults.First();
            for (var index = 0; index < results.Count; index++)
            {
                var txt = results[index].Info;
                var x = participantResults.Select(l => l.First(r => r.Info == txt))
                         .OrderBy((r) => r.Duration);

                var max = x.Last();
                max.Id = Guid.NewGuid();
                max.IsMax = true;
                dbResults.Save(max);
            }
        }

        private MongoEntities.Session CreateNewSession(int participantsNo)
        {
            var clientMongo = GetMongoDatabase();
            var dbCollection = clientMongo.GetCollection<MongoEntities.Session>(typeof(MongoEntities.Session).Name);

            var maxSesion = (from session in dbCollection.AsQueryable()
                             orderby session.Id descending
                             select session).FirstOrDefault();

            var currentSession = new MongoEntities.Session();
            currentSession.Id = maxSesion == null ? 0 : maxSesion.Id + 1;
            currentSession.ParticipantsNo = participantsNo;
            currentSession.Timestamp = DateTime.UtcNow;

            dbCollection.Save(currentSession);
            return currentSession;
        }

        private List<MongoEntities.RawQueryResult> Test(int index, string discriminator, Barrier barrier, MongoEntities.Session session, ITestContext testContext)
        {

            var many = new[] { "a", "gas", "bhd", "group", "e", "i", "o", "u", "g", "p" };
            var some = new[] { "apple", "aircraft", "argo", "agrium", "building", "electrical", "foods", "ireland", "king", "health" }; //"electronics","co","bancorp" , 
            var none = new[] { ".*a", "string index barrier", ".apple.113", "1b23", "xyzt", "mongo entities long text", "'aa'dadada", "9993", "3018882", "r chart, format the bottom h" };

            //match many

            List<Stack<string>> tests = new List<Stack<string>>();
            tests.Add(new Stack<string>(Reorganize(index, many)));
            tests.Add(new Stack<string>(Reorganize(index, some)));
            tests.Add(new Stack<string>(Reorganize(index, none)));
            var list = new List<MongoEntities.RawQueryResult>();


            while (true)
            {


                bool executed = false;
                foreach (var stack in tests)
                {
                    var element = stack.Count > 0 ? stack.Pop() : null;
                    if (element != null)
                    {
                        Thread.Sleep(200);
                        barrier.SignalAndWait();
                        var result = testContext.TestRaw(element, discriminator, session);
                        list.Add(result);
                        executed = true;
                    }

                }

                if (!executed)
                    break;
            }

            Console.WriteLine("completed " + discriminator);
            return list;
        }


        private static IEnumerable<string> Reorganize(int index, string[] many)
        {
            var startWith = index % many.Length;
            return many.Skip(startWith).Union(many.Take(startWith));
        }


        private MongoDatabase GetMongoDatabase()
        {
            return this.resultsDb;
        }
    }


}
